// Created by Glif.
#include <stm32f4xx_ll_gpio.h>
#include <stm32f4xx_ll_spi.h>
#include <stm32f4xx_ll_utils.h>
#include "st7735.h"

#define CS_RESET  LL_GPIO_ResetOutputPin(g_lcdsInit.CS_GPIO_Port, g_lcdsInit.CS_Pin)
#define CS_SET    LL_GPIO_SetOutputPin(g_lcdsInit.CS_GPIO_Port, g_lcdsInit.CS_Pin)
#define DC_RESET  LL_GPIO_ResetOutputPin(g_lcdsInit.DC_GPIO_Port, g_lcdsInit.DC_Pin)
#define DC_SET    LL_GPIO_SetOutputPin(g_lcdsInit.DC_GPIO_Port, g_lcdsInit.DC_Pin)
#define RST_RESET LL_GPIO_ResetOutputPin(g_lcdsInit.RST_GPIO_Port, g_lcdsInit.RST_Pin)
#define RST_SET   LL_GPIO_SetOutputPin(g_lcdsInit.RST_GPIO_Port, g_lcdsInit.RST_Pin)

static const uint8_t g_ppu8Ascii[95][16] = {
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //   0
    {0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00}, // ! 1
    {0x12, 0x24, 0x24, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // " 2
    {0x00, 0x00, 0x12, 0x12, 0x12, 0x7E, 0x24, 0x24, 0x24, 0x7E, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00}, // # 3
    {0x00, 0x08, 0x3C, 0x4A, 0x4A, 0x48, 0x38, 0x0C, 0x0A, 0x0A, 0x4A, 0x4A, 0x3C, 0x08, 0x08, 0x00}, // $ 4
    {0x00, 0x00, 0x44, 0xA4, 0xA8, 0xA8, 0xB0, 0x54, 0x1A, 0x2A, 0x2A, 0x4A, 0x44, 0x00, 0x00, 0x00}, // % 5
    {0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x50, 0x6E, 0xA4, 0x94, 0x98, 0x89, 0x76, 0x00, 0x00, 0x00}, // & 6
    {0x60, 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ' 7
    {0x02, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x02, 0x00, 0x00}, // ( 8
    {0x40, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x40, 0x00, 0x00}, // ) 9
    {0x00, 0x00, 0x00, 0x10, 0x10, 0xD6, 0x38, 0x38, 0xD6, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, // * 10
    {0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x7F, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, // + 11
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x20, 0x40, 0x00}, // , 12
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // - 13
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00}, // . 14
    {0x00, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x00, 0x00}, // / 15
    {0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x00}, // 0 16
    {0x00, 0x00, 0x08, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, 0x00}, // 1 17
    {0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x02, 0x04, 0x08, 0x10, 0x20, 0x42, 0x7E, 0x00, 0x00, 0x00}, // 2 18
    {0x00, 0x00, 0x3C, 0x42, 0x42, 0x02, 0x04, 0x18, 0x04, 0x02, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x00}, // 3 19
    {0x00, 0x00, 0x04, 0x0C, 0x0C, 0x14, 0x24, 0x24, 0x44, 0x7F, 0x04, 0x04, 0x1F, 0x00, 0x00, 0x00}, // 4 20
    {0x00, 0x00, 0x7E, 0x40, 0x40, 0x40, 0x78, 0x44, 0x02, 0x02, 0x42, 0x44, 0x38, 0x00, 0x00, 0x00}, // 5 21
    {0x00, 0x00, 0x18, 0x24, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x42, 0x22, 0x1C, 0x00, 0x00, 0x00}, // 6 22
    {0x00, 0x00, 0x7E, 0x42, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00}, // 7 23
    {0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x00}, // 8 24
    {0x00, 0x00, 0x38, 0x44, 0x42, 0x42, 0x42, 0x46, 0x3A, 0x02, 0x02, 0x24, 0x18, 0x00, 0x00, 0x00}, // 9 25
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, // : 26
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00}, // ; 27
    {0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, 0x00}, // < 28
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // = 29
    {0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00}, // > 30
    {0x00, 0x00, 0x3C, 0x42, 0x42, 0x62, 0x04, 0x08, 0x08, 0x08, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, // ? 31
    {0x00, 0x00, 0x38, 0x44, 0x5A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x5C, 0x42, 0x3C, 0x00, 0x00, 0x00}, // @ 32
    {0x00, 0x00, 0x10, 0x10, 0x18, 0x28, 0x28, 0x24, 0x3C, 0x44, 0x42, 0x42, 0xE7, 0x00, 0x00, 0x00}, // A 33
    {0x00, 0x00, 0xF8, 0x44, 0x44, 0x44, 0x78, 0x44, 0x42, 0x42, 0x42, 0x44, 0xF8, 0x00, 0x00, 0x00}, // B 34
    {0x00, 0x00, 0x3E, 0x42, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x44, 0x38, 0x00, 0x00, 0x00}, // C 35
    {0x00, 0x00, 0xF8, 0x44, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x44, 0xF8, 0x00, 0x00, 0x00}, // D 36
    {0x00, 0x00, 0xFC, 0x42, 0x48, 0x48, 0x78, 0x48, 0x48, 0x40, 0x42, 0x42, 0xFC, 0x00, 0x00, 0x00}, // E 37
    {0x00, 0x00, 0xFC, 0x42, 0x48, 0x48, 0x78, 0x48, 0x48, 0x40, 0x40, 0x40, 0xE0, 0x00, 0x00, 0x00}, // F 38
    {0x00, 0x00, 0x3C, 0x44, 0x44, 0x80, 0x80, 0x80, 0x8E, 0x84, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00}, // G 39
    {0x00, 0x00, 0xE7, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0xE7, 0x00, 0x00, 0x00}, // H 40
    {0x00, 0x00, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00}, // I 41
    {0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x88, 0xF0, 0x00}, // J 42
    {0x00, 0x00, 0xEE, 0x44, 0x48, 0x50, 0x70, 0x50, 0x48, 0x48, 0x44, 0x44, 0xEE, 0x00, 0x00, 0x00}, // K 43
    {0x00, 0x00, 0xE0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0xFE, 0x00, 0x00, 0x00}, // L 44
    {0x00, 0x00, 0xEE, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x54, 0x54, 0x54, 0x54, 0xD6, 0x00, 0x00, 0x00}, // M 45
    {0x00, 0x00, 0xC7, 0x62, 0x62, 0x52, 0x52, 0x4A, 0x4A, 0x4A, 0x46, 0x46, 0xE2, 0x00, 0x00, 0x00}, // N 46
    {0x00, 0x00, 0x38, 0x44, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x44, 0x38, 0x00, 0x00, 0x00}, // O 47
    {0x00, 0x00, 0xFC, 0x42, 0x42, 0x42, 0x42, 0x7C, 0x40, 0x40, 0x40, 0x40, 0xE0, 0x00, 0x00, 0x00}, // P 48
    {0x00, 0x00, 0x38, 0x44, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xB2, 0x4C, 0x38, 0x06, 0x00, 0x00}, // Q 49
    {0x00, 0x00, 0xFC, 0x42, 0x42, 0x42, 0x7C, 0x48, 0x48, 0x44, 0x44, 0x42, 0xE3, 0x00, 0x00, 0x00}, // R 50
    {0x00, 0x00, 0x3E, 0x42, 0x42, 0x40, 0x20, 0x18, 0x04, 0x02, 0x42, 0x42, 0x7C, 0x00, 0x00, 0x00}, // S 51
    {0x00, 0x00, 0xFE, 0x92, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00}, // T 52
    {0x00, 0x00, 0xE7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x00}, // U 53
    {0x00, 0x00, 0xE7, 0x42, 0x42, 0x44, 0x24, 0x24, 0x28, 0x28, 0x18, 0x10, 0x10, 0x00, 0x00, 0x00}, // V 54
    {0x00, 0x00, 0xD6, 0x54, 0x54, 0x54, 0x54, 0x54, 0x6C, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00}, // W 55
    {0x00, 0x00, 0xE7, 0x42, 0x24, 0x24, 0x18, 0x18, 0x18, 0x24, 0x24, 0x42, 0xE7, 0x00, 0x00, 0x00}, // X 56
    {0x00, 0x00, 0xEE, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00}, // Y 57
    {0x00, 0x00, 0x7E, 0x84, 0x04, 0x08, 0x08, 0x10, 0x20, 0x20, 0x42, 0x42, 0xFC, 0x00, 0x00, 0x00}, // Z 58
    {0x1E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x00, 0x00}, // [ 59
    {0x00, 0x40, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00}, // \ 60
    {0x78, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, 0x00}, // ] 61
    {0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ^ 62
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00}, // _ 63
    {0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ` 64
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x0C, 0x34, 0x44, 0x4C, 0x36, 0x00, 0x00, 0x00}, // a 65
    {0x00, 0x00, 0x00, 0xC0, 0x40, 0x40, 0x58, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x00, 0x00, 0x00}, // b 66
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1C, 0x00, 0x00, 0x00}, // c 67
    {0x00, 0x00, 0x00, 0x06, 0x02, 0x02, 0x3E, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3B, 0x00, 0x00, 0x00}, // d 68
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x40, 0x42, 0x3C, 0x00, 0x00, 0x00}, // e 69
    {0x00, 0x00, 0x00, 0x0C, 0x12, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00}, // f 70
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x44, 0x44, 0x38, 0x40, 0x3C, 0x42, 0x42, 0x3C, 0x00}, // g 71
    {0x00, 0x00, 0x00, 0xC0, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x42, 0x42, 0xE7, 0x00, 0x00, 0x00}, // h 72
    {0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00}, // i 73
    {0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x78, 0x00}, // j 74
    {0x00, 0x00, 0x00, 0xC0, 0x40, 0x40, 0x4E, 0x48, 0x50, 0x70, 0x48, 0x44, 0xEE, 0x00, 0x00, 0x00}, // k 75
    {0x00, 0x00, 0x10, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00}, // l 76
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x49, 0x49, 0x49, 0x49, 0x49, 0xED, 0x00, 0x00, 0x00}, // m 77
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x62, 0x42, 0x42, 0x42, 0x42, 0xE7, 0x00, 0x00, 0x00}, // n 78
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x00}, // o 79
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x40, 0xE0, 0x00}, // p 80
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x26, 0x42, 0x42, 0x42, 0x26, 0x1A, 0x02, 0x07, 0x00}, // q 81
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x32, 0x20, 0x20, 0x20, 0x20, 0xF8, 0x00, 0x00, 0x00}, // r 82
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x42, 0x40, 0x3C, 0x02, 0x42, 0x7C, 0x00, 0x00, 0x00}, // s 83
    {0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x12, 0x0C, 0x00, 0x00, 0x00}, // t 84
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3B, 0x00, 0x00, 0x00}, // u 85
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x00, 0x00, 0x00}, // v 86
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x89, 0x4A, 0x5A, 0x54, 0x24, 0x24, 0x00, 0x00, 0x00}, // w 87
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x24, 0x18, 0x18, 0x18, 0x24, 0x6E, 0x00, 0x00, 0x00}, // x 88
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x42, 0x24, 0x24, 0x18, 0x18, 0x10, 0x10, 0x60, 0x00}, // y 89
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x44, 0x08, 0x10, 0x10, 0x22, 0x7E, 0x00, 0x00, 0x00}, // z 90
    {0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00}, // { 91
    {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x80}, // | 92
    {0xC0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0xC0, 0x00, 0x00}, // } 93
    {0x5A, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}  // ~ 94
};
static lcdSettings g_lcdsInit;
static lcdMeasurement g_lcdmImage = {0, 0, 2, LCD_X * 2, LCD_X, LCD_Y, 0};
static uint16_t g_u16ForeColor = COLOR_BLACK, g_u16BackColor = COLOR_WHITE;

static void lcdWriteCommand(uint8_t u8Command) {
    DC_RESET;
    while (!LL_SPI_IsActiveFlag_TXE(g_lcdsInit.SPIx));
    LL_SPI_TransmitData8(g_lcdsInit.SPIx, u8Command);
    while (!LL_SPI_IsActiveFlag_RXNE(g_lcdsInit.SPIx));
    LL_SPI_ReceiveData8(g_lcdsInit.SPIx);
}

static void lcdWriteData8(uint8_t u8Data) {
    DC_SET;
    while (!LL_SPI_IsActiveFlag_TXE(g_lcdsInit.SPIx));
    LL_SPI_TransmitData8(g_lcdsInit.SPIx, u8Data);
    while (!LL_SPI_IsActiveFlag_RXNE(g_lcdsInit.SPIx));
    LL_SPI_ReceiveData8(g_lcdsInit.SPIx);
}

static void lcdWriteData16(uint16_t u16Data) {
    DC_SET;
    while (!LL_SPI_IsActiveFlag_TXE(g_lcdsInit.SPIx));
    LL_SPI_TransmitData8(g_lcdsInit.SPIx, u16Data >> 8);
    while (!LL_SPI_IsActiveFlag_RXNE(g_lcdsInit.SPIx));
    LL_SPI_ReceiveData8(g_lcdsInit.SPIx);
    while (!LL_SPI_IsActiveFlag_TXE(g_lcdsInit.SPIx));
    LL_SPI_TransmitData8(g_lcdsInit.SPIx, u16Data & 0xFF);
    while (!LL_SPI_IsActiveFlag_RXNE(g_lcdsInit.SPIx));
    LL_SPI_ReceiveData8(g_lcdsInit.SPIx);
}

static void lcdInitArea(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
    lcdWriteCommand(0x2A);
    lcdWriteData8(0x00);
    #if (LCD_DIRECTION > 1)
        lcdWriteData8(xStart + 1);
        lcdWriteData8(0x00);
        lcdWriteData8(xEnd + 1);
        lcdWriteCommand(0x2B);
        lcdWriteData8(0x00);
        lcdWriteData8(yStart + 2);
        lcdWriteData8(0x00);
        lcdWriteData8(yEnd + 2);	
    #else
        lcdWriteData8(xStart + 2);
        lcdWriteData8(0x00);
        lcdWriteData8(xEnd + 2);
        lcdWriteCommand(0x2B);
        lcdWriteData8(0x00);
        lcdWriteData8(yStart + 1);
        lcdWriteData8(0x00);
        lcdWriteData8(yEnd + 1);	
    #endif
    lcdWriteCommand(0x2C);
}

void lcdClear(void) {
    CS_RESET;
    lcdInitArea(0, 0, LCD_X - 1, LCD_Y - 1);
    for (uint16_t i = 0, j; i < LCD_X; ++i) for (j = 0; j < LCD_Y; ++j) lcdWriteData16(g_u16BackColor);
    CS_SET;
}

void lcdInit(lcdSettings *lcdsInit) {
    g_lcdsInit.SPIx = lcdsInit->SPIx;
    g_lcdsInit.CS_GPIO_Port = lcdsInit->CS_GPIO_Port;
    g_lcdsInit.DC_GPIO_Port = lcdsInit->DC_GPIO_Port;
    g_lcdsInit.RST_GPIO_Port = lcdsInit->RST_GPIO_Port;
    g_lcdsInit.CS_Pin = lcdsInit->CS_Pin;
    g_lcdsInit.DC_Pin = lcdsInit->DC_Pin;
    g_lcdsInit.RST_Pin = lcdsInit->RST_Pin;
    LL_SPI_Enable(g_lcdsInit.SPIx);
    RST_RESET;
    LL_mDelay(10);
    RST_SET;
    LL_mDelay(120);
    CS_RESET;
    lcdWriteCommand(0x11);
    LL_mDelay(120);
    lcdWriteCommand(0xB1);
    lcdWriteData8(0x01);
    lcdWriteData8(0x2C);
    lcdWriteData8(0x2D);
    lcdWriteCommand(0xB2);
    lcdWriteData8(0x01);
    lcdWriteData8(0x2C);
    lcdWriteData8(0x2D);
    lcdWriteCommand(0xB3);
    lcdWriteData8(0x01);
    lcdWriteData8(0x2C);
    lcdWriteData8(0x2D);
    lcdWriteData8(0x01);
    lcdWriteData8(0x2C);
    lcdWriteData8(0x2D);
    lcdWriteCommand(0xB4);
    lcdWriteData8(0x07);
    lcdWriteCommand(0xC0);
    lcdWriteData8(0xA2);
    lcdWriteData8(0x02);
    lcdWriteData8(0x84);
    lcdWriteCommand(0xC1);
    lcdWriteData8(0xC5);
    lcdWriteCommand(0xC2);
    lcdWriteData8(0x0A);
    lcdWriteData8(0x00);
    lcdWriteCommand(0xC3);
    lcdWriteData8(0x8A);
    lcdWriteData8(0x2A);
    lcdWriteCommand(0xC4);
    lcdWriteData8(0x8A);
    lcdWriteData8(0xEE);
    lcdWriteCommand(0xC5);
    lcdWriteData8(0x0E);
    lcdWriteCommand(0x36);
    switch (LCD_DIRECTION) {
        case 0: lcdWriteData8(0xC0); break;
        case 1: lcdWriteData8(0x00); break;
        case 2: lcdWriteData8(0xA0); break;
        case 3: lcdWriteData8(0x60);
    }
    lcdWriteCommand(0xE0);
    lcdWriteData8(0x0F);
    lcdWriteData8(0x1A);
    lcdWriteData8(0x0F);
    lcdWriteData8(0x18);
    lcdWriteData8(0x2F);
    lcdWriteData8(0x28);
    lcdWriteData8(0x20);
    lcdWriteData8(0x22);
    lcdWriteData8(0x1F);
    lcdWriteData8(0x1B);
    lcdWriteData8(0x23);
    lcdWriteData8(0x37);
    lcdWriteData8(0x00);
    lcdWriteData8(0x07);
    lcdWriteData8(0x02);
    lcdWriteData8(0x10);
    lcdWriteCommand(0xE1);
    lcdWriteData8(0x0F);
    lcdWriteData8(0x1B);
    lcdWriteData8(0x0F);
    lcdWriteData8(0x17);
    lcdWriteData8(0x33);
    lcdWriteData8(0x2C);
    lcdWriteData8(0x29);
    lcdWriteData8(0x2E);
    lcdWriteData8(0x30);
    lcdWriteData8(0x30);
    lcdWriteData8(0x39);
    lcdWriteData8(0x3F);
    lcdWriteData8(0x00);
    lcdWriteData8(0x07);
    lcdWriteData8(0x03);
    lcdWriteData8(0x10);
    lcdWriteCommand(0x2A);
    lcdWriteData8(0x00);
    lcdWriteData8(0x02);
    lcdWriteData8(0x00);
    lcdWriteData8(0x82);
    lcdWriteCommand(0x2B);
    lcdWriteData8(0x00);
    lcdWriteData8(0x03);
    lcdWriteData8(0x00);
    lcdWriteData8(0x83);
    lcdWriteCommand(0xF0);
    lcdWriteData8(0x01);
    lcdWriteCommand(0xF6);
    lcdWriteData8(0x00);
    lcdWriteCommand(0x3A);
    lcdWriteData8(0x05);
    lcdWriteCommand(0x29);
    CS_SET;
    lcdClear();
}

void lcdSetForeColor(uint16_t u16ForeColor) {g_u16ForeColor = u16ForeColor;}

void lcdSetBackColor(uint16_t u16BackColor) {g_u16BackColor = u16BackColor;}

void lcdDrawPoint(uint16_t x, uint16_t y) {
    CS_RESET;
    lcdInitArea(x, y, x, y);
    lcdWriteData16(g_u16ForeColor);
    CS_SET;
}

void lcdDrawRect(uint16_t ax, uint16_t ay, uint16_t bx, uint16_t by, _Bool bFill) {
    if (ax > bx) ax ^ bx && (bx ^= ax ^= bx, ax ^= bx);
    if (ay > by) ay ^ by && (by ^= ay ^= by, ay ^= by);
    --bx, --by;
    uint16_t pos = ay;
    if (bFill) {
        do {
            do lcdDrawPoint(ax, ay); while (ay++ < by);
            ay = pos;
        } while (ax++ < bx);
    }
    else {
        do {
            lcdDrawPoint(ax, ay);
            lcdDrawPoint(bx, ay);
        } while (ay++ < by);
        ay = pos;
        do {
            lcdDrawPoint(ax, ay);
            lcdDrawPoint(ax, by);
        } while (ax++ < bx);
    }
}

void lcdDrawChar(uint16_t x, uint16_t y, uint8_t u8Data) {
    CS_RESET;
    lcdInitArea(x, y, x + 7, y + 15);
    for (uint8_t j = 0, i, pos; j < 16; ++j) {
        pos = *(*(g_ppu8Ascii + u8Data - ' ') + j);
        i = 8;
        while (i--) lcdWriteData16(pos & 1 << i ? g_u16ForeColor : g_u16BackColor);
    }
    CS_SET;
}

void lcdDrawStr(uint16_t x, uint16_t y, const uint8_t *pu8Data) {
    uint16_t i = 0;
    while (*(pu8Data + i) != '\0') lcdDrawChar(x + i * 8, y, *(pu8Data + i)), ++i;
}

void lcdDrawInt8(uint16_t x, uint16_t y, int8_t i8Data) {
    uint8_t pu8Order[3];
    lcdDrawChar(x, y, i8Data >= 0 ? (*pu8Order = i8Data, ' ') : (*pu8Order = -i8Data, '-'));
    i8Data = 2;
    do *(pu8Order + i8Data) = *pu8Order % 10, *pu8Order /= 10; while (*pu8Order && --i8Data);
    while (i8Data < 3) lcdDrawChar(x += 8, y, *(pu8Order + i8Data++) + '0');
}

void lcdDrawUInt8(uint16_t x, uint16_t y, uint8_t u8Data) {
    uint8_t pu8Order[3];
    x -= 8, *pu8Order = u8Data, u8Data = 2;
    do *(pu8Order + u8Data) = *pu8Order % 10, *pu8Order /= 10; while (*pu8Order && --u8Data);
    while (u8Data < 3) lcdDrawChar(x += 8, y, *(pu8Order + u8Data++) + '0');
}

void lcdDrawInt16(uint16_t x, uint16_t y, int16_t i16Data) {
    uint16_t pu16Order[5];
    lcdDrawChar(x, y, i16Data >= 0 ? (*pu16Order = i16Data, ' ') : (*pu16Order = -i16Data, '-'));
    i16Data = 4;
    do *(pu16Order + i16Data) = *pu16Order % 10, *pu16Order /= 10; while (*pu16Order && --i16Data);
    while (i16Data < 5) lcdDrawChar(x += 8, y, *(pu16Order + i16Data++) + '0');
}

void lcdDrawUInt16(uint16_t x, uint16_t y, uint16_t u16Data) {
    uint16_t pu16Order[5];
    x -= 8, *pu16Order = u16Data, u16Data = 4;
    do *(pu16Order + u16Data) = *pu16Order % 10, *pu16Order /= 10; while (*pu16Order && --u16Data);
    while (u16Data < 5) lcdDrawChar(x += 8, y, *(pu16Order + u16Data++) + '0');
}

void lcdDrawChinese(uint16_t x, uint16_t y, const uint8_t *pu8Data, uint8_t u8Size, uint8_t u8Length) {
    CS_RESET;
    lcdInitArea(x, y, x + u8Size * u8Length - 1, y + u8Size - 1);
    uint8_t i, j;
    const uint8_t *pu8Row;
    for (u8Size /= 8, x = 0; x < u8Size * 8; ++x) {
        i = u8Length;
        pu8Row = pu8Data + x * u8Size;
        while (i--) {
            for (y = 0; y < u8Size; ++y) {
                j = 8;
                while (j--) lcdWriteData16(*pu8Row & 1 << j ? g_u16ForeColor : g_u16BackColor);
                ++pu8Row;
            }
            pu8Row += u8Size * (u8Size * 8 - 1);
        }
    }
    CS_SET;
}

void lcdSetImageScale(lcdMeasurement *lcdmImage) {
    g_lcdmImage.x = lcdmImage->x;
    g_lcdmImage.y = lcdmImage->y;
    g_lcdmImage.u16Width = lcdmImage->u16Width / lcdmImage->u16ZoomWidth & -2;
    if (!g_lcdmImage.u16Width) ++g_lcdmImage.u16Width;
    g_lcdmImage.u16ZoomWidth = lcdmImage->u16Width / g_lcdmImage.u16Width;
    g_lcdmImage.u16Width *= 2;
    g_lcdmImage.u16Height = lcdmImage->u16Height / lcdmImage->u16ZoomHeight & -2;
    if (!g_lcdmImage.u16Height) ++g_lcdmImage.u16Height;
    g_lcdmImage.u16ZoomHeight = lcdmImage->u16Height / g_lcdmImage.u16Height;
    g_lcdmImage.u16Height *= lcdmImage->u16Width * 2;
    g_lcdmImage.bBigEndian = lcdmImage->bBigEndian;
}

void lcdDrawImage(const uint8_t *pu8Data) {
    CS_RESET;
    lcdInitArea(g_lcdmImage.x, g_lcdmImage.y, g_lcdmImage.x + g_lcdmImage.u16ZoomWidth - 1, g_lcdmImage.y + g_lcdmImage.u16ZoomHeight - 1);
    const uint8_t *pos;
    for (uint16_t j = 0, i; j < g_lcdmImage.u16ZoomHeight; ++j) {
        for (i = 0; i < g_lcdmImage.u16ZoomWidth; ++i) {
            pos = pu8Data + j * g_lcdmImage.u16Height + i * g_lcdmImage.u16Width;
            lcdWriteData16(g_lcdmImage.bBigEndian ? *pos << 8 | *(pos + 1) : *pos | *(pos + 1) << 8);
        }
    }
    CS_SET;
}
